package lapsnake.settings;


import java.awt.Dimension;
import java.awt.Point;
import java.awt.Rectangle;

import lapsnake.main.LapSnake;
import lapsnake.model.data.Property;
import owg.shallowserializer.GenericArrayParser;
import owg.shallowserializer.ShallowSerializer;
import owg.shallowserializer.ShallowSerializer.Comment;
import owg.shallowserializer.ShallowSerializer.ToString;
import owg.shallowserializer.TypeParser;


public class Settings
{
    private static final String SETTINGS_FILE = "settings/settings.txt";
    static ShallowSerializer<Settings> serializer = new ShallowSerializer<Settings>(Settings.class);

    static
    {
	ShallowSerializer.registerInternalSerializer(Rectangle.class, (Rectangle r) -> {return ""+r.x+','+r.y+','+r.width+','+r.height;});
	ShallowSerializer.registerInternalParser(Rectangle.class, new TypeParser.Parser<Rectangle>() {
	    GenericArrayParser<Integer> iArrayParser = new GenericArrayParser<>(Integer.class, TypeParser.intParser, 4);
	    @Override
	    public Rectangle parse(Class<Rectangle> type, String str, Rectangle def) throws Exception
	    {
		Integer[] a = iArrayParser.parse(Integer[].class, str, null);
		return new Rectangle(a[0],a[1],a[2],a[3]);
	    }
	});
	ShallowSerializer.registerInternalSerializer(Point.class, (Point r) -> {return ""+r.x+','+r.y;});
	ShallowSerializer.registerInternalParser(Point.class, new TypeParser.Parser<Point>() {
	    GenericArrayParser<Integer> iArrayParser = new GenericArrayParser<>(Integer.class, TypeParser.intParser, 4);
	    @Override
	    public Point parse(Class<Point> type, String str, Point def) throws Exception
	    {
		Integer[] a = iArrayParser.parse(Integer[].class, str, null);
		return new Point(a[0],a[1]);
	    }
	});
	ShallowSerializer.registerInternalSerializer(Dimension.class, (Dimension r) -> {return ""+r.width+','+r.height;});
	ShallowSerializer.registerInternalParser(Dimension.class, new TypeParser.Parser<Dimension>() {
	    GenericArrayParser<Integer> iArrayParser = new GenericArrayParser<>(Integer.class, TypeParser.intParser, 4);
	    @Override
	    public Dimension parse(Class<Dimension> type, String str, Dimension def) throws Exception
	    {
		Integer[] a = iArrayParser.parse(Integer[].class, str, null);
		return new Dimension(a[0],a[1]);
	    }
	});
	
	@SuppressWarnings("rawtypes")
	ToString<Property> propertySerializer = new ToString<Property>() {
	    @Override
	    public String toString(Property p) {
	        return p.getType().getName()+":"+ShallowSerializer.toString(p.getType(), p.get());
	    }
	};
	ShallowSerializer.registerInternalSerializer(Property.class, propertySerializer);
	@SuppressWarnings({ "unchecked", "rawtypes" })
	TypeParser.Parser<Property> propertyParser = new TypeParser.Parser<Property>() {
	    
	    @Override
	    public Property parse(Class<Property> type, String str, Property def) throws Exception {
		int colonPos = str.indexOf(':');
		if(colonPos == -1)
		    throw new IllegalArgumentException(str+" is not a property because it has no colon...");
		String className = str.substring(0, colonPos).trim();
		Class<?> internalType = Class.forName(className);
		if(def != null && def.getType() != internalType)
		    throw new IllegalArgumentException("Class name in serialized file was: "+internalType+" but default class name is: "+def.getType());
		String valueStr = str.substring(colonPos+1).trim();
		Object internalValue = TypeParser.parse(internalType, valueStr);
		return new Property(internalType, internalValue);
	    }
	};
	ShallowSerializer.registerInternalParser(Property.class, propertyParser);
    }

    @Comment("The bounds of the game window, when in windowed mode.")
    public Rectangle windowedBounds =  null;
    @Comment("Whether the game should run in fullscreen mode.")
    public boolean isFullscreen = false;
    @Comment("Whether only power-of-two textures should be used(compatibility).")
    public boolean forcePOTS = false;
    @Comment("How large the audio buffer should be(only when useOpenAL is false). Causes latency, but reduces glitches.")
    public int jsAudioBufferMultiplier = 8;
    @Comment("Whether to use OpenAL(performance). Set to false to use JavaSound(compatibility).")
    public boolean useOpenAL = false;
    @Comment("Whether to synchronize frame rate with vertical blanks to avoid tearing of the image. Reduces maximum potential frame rate.")
    public boolean verticalSync = false;
    @Comment("Whether to enable visual editing by default.")
    public Property<Boolean> visualMode = new Property<>(true);
    @Comment("How many spaces to use for an indentation level.")
    public Property<Integer> indentSpaces = new Property<>(4); 

    private void copyFromProgram()
    {
	windowedBounds = LapSnake.frame.getWindowedBounds();
	isFullscreen = LapSnake.frame.isFullscreen();
    }

    public static Settings open()
    {
	return serializer.read(SETTINGS_FILE);
    }

    public void save()
    {
	copyFromProgram();
	serializer.write(SETTINGS_FILE, this);
    }
}
